#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

enum
{ // 定义寄存器
    A,
    B,
    C,
    IP,
    SP,
    FLAG,
    REG_SIZE
};

enum
{ // 定义操作码
    // 基础指令
    GPT = 10,
    GLD,
    SET,
    NOP,
    // 栈指令
    PSH = 20,
    POP,
    // 算术指令
    ADD = 30,
    SUB,
    DIV,
    MUL,
    // 控制指令
    EQ = 40,
    NEQ,
    LT,
    LTE,
    HALT,
    // 分支指令
    JMP = 50,
    JMPN
};

#define INSTRUCTION_SIZE 100 // 存取指令的区域大小，此虚拟机使用数组存储
#define STACK_SIZE 100       // 栈大小
#define SENTINEL_VALUE 10000 // 哨兵值，当用户输入该值，则停止接收用户输入

static int instructions[INSTRUCTION_SIZE] = {0};
static int registers[REG_SIZE] = {0};
static int stack[STACK_SIZE] = {0};
static bool is_runing = true;

void printRegisters();
bool isZeroError(int operateNumber);
bool isAccumulatorError(int accumulator);
bool isOperateCodeError(int operateCode);
void execute(int instruction);

const char *registerToString(int reg)
{
    const char *result;
    switch (reg)
    {
    case A:
    {
        result = "REG_A";
        break;
    }
    case B:
    {
        result = "REG_B";
        break;
    }
    case C:
    {
        result = "REG_C";
        break;
    }
    case IP:
    {
        result = "REG_IP";
        break;
    }
    case SP:
    {
        result = "REG_SP";
        break;
    }
    default: // 非法寄存器
    {
        printf("unknow register: %d\n", reg);
        break;
    }
    }
    return result;
}

int main(int argc, char *argv[])
{
    registers[IP] = 0; // 指令指针重置为0
    registers[SP] = -1; // 栈指针寄存器初始化为-1
    instructions[registers[IP]] = 0; // 再次初始化为0

    printf("*** Hi there!***\n"
           "*** 请每次输入一条指令或一条数据 ***\n"
           "*** 在指令或数据前将会打上位置编号和‘？’ ***\n"
           "*** 然后在该位置输入对应的一个‘word’ ***\n"
           "*** 当输入哨兵值 - 10000 时，停止输入 ***\n"
           "*** your program. ***\n\n");

    printRegisters();

    while (registers[IP] < INSTRUCTION_SIZE && instructions[registers[IP] - 1] != SENTINEL_VALUE)
    {
        scanf("%d", &instructions[registers[IP]++]);
    }

    int instruction_length = registers[IP]; // 保存用户输入的指令长度

    registers[IP] = 0;
    printf("instruction length is: %d\n", instruction_length);
    while (is_runing && registers[IP] < instruction_length)
    {
        execute(instructions[registers[IP]]);
    }

    printRegisters();

    return 0;
}

void execute(int instruction)
{
    printf("%d,%d\n", instruction, registers[IP]);
    switch (instruction)
    {
        case GPT: // GPT REG; 将栈顶的值保存到寄存器中
        {
            instructions[registers[IP]++] = stack[registers[SP] + 1];
            registers[IP] += 2;
            break;
        }
        case GLD: // GLD REG; 将寄存器中的值保存到栈顶
        {
            stack[++registers[SP]] = instructions[registers[IP] + 1];
            registers[IP] += 2;
            break;
        }
        case SET: // SET REG, VAL; 将一个值赋值给一个寄存器
        {
            registers[instructions[registers[IP] + 1]] = instructions[registers[IP] + 2];
            registers[IP] += 2;
            break;
        }
        case PSH: // PSH VAL; 将值入栈顶
        {
            stack[++registers[SP]] = instructions[registers[IP] + 1];
            registers[IP] += 2;
            break;
        }
        case POP: // POP; 将栈顶值弹出
        {
            int val = stack[registers[SP]--];
            registers[IP]++;
            printf("pop %d\n", val);
            break;
        }
        case ADD: // 将栈顶两个值相加，结果保存到栈顶
        {
            registers[A] = stack[registers[SP]--];
            registers[B] = stack[registers[SP]--];
            registers[C] = registers[A] + registers[B];
            stack[++registers[SP]] = registers[C];
            registers[IP]++;
            break;
        }
        case SUB: // 将栈顶两个值相减，结果保存到栈顶
        {
            registers[A] = stack[registers[SP]--];
            registers[B] = stack[registers[SP]--];
            registers[C] = registers[A] - registers[B];
            stack[++registers[SP]] = registers[C];
            registers[IP]++;
            break;
        }
        case MUL: // 将栈顶两个值相乘，结果保存到栈顶
        {
            registers[A] = stack[registers[SP]--];
            registers[B] = stack[registers[SP]--];
            registers[C] = registers[A] * registers[B];
            stack[++registers[SP]] = registers[C];
            registers[IP]++;
            break;
        }
        case DIV: // 将栈顶两个值相除，结果保存到栈顶
        {
            registers[A] = stack[registers[SP]--];
            registers[B] = stack[registers[SP]--];
            registers[C] = registers[A] / registers[B];
            stack[++registers[SP]] = registers[C];
            registers[IP]++;
            break;
        }
        case EQ: // 判断两个寄存器值值否相等，结果会影响FLAG寄存器
        {
            if (registers[instructions[registers[IP] + 1]] == registers[instructions[registers[IP] + 2]]) {
                registers[FLAG] = 1;
            }
            else
            {
                registers[FLAG] = 0;
            }
            registers[IP] += 2;
            break;
        }
        case NEQ: // 判断两个寄存器值否不想等，结果会影响FLAG寄存器
        {
            if (registers[instructions[registers[IP] + 1]] != registers[instructions[registers[IP] + 2]])
            {
                registers[FLAG] = 1;
            }
            else
            {
                registers[FLAG] = 0;
            }
            registers[IP] += 2;
            break;
        }
        case LT: // LT A,B 比较寄存器A是否小于寄存器B，结果会影响FLAG寄存器
        {
            if (registers[instructions[registers[IP] + 1]] < registers[instructions[registers[IP] + 2]])
            {
                registers[FLAG] = 1;
            }
            else
            {
                registers[FLAG] = 0;
            }
            registers[IP] += 2;
            break;
        }
        case LTE: // LTE A,B 比较寄存器A是否小于等于寄存器B，结果会影响FLAG寄存器
        {
            if (registers[instructions[registers[IP] + 1]] <= registers[instructions[registers[IP] + 2]])
            {
                registers[FLAG] = 1;
            }
            else
            {
                registers[FLAG] = 0;
            }
            registers[IP] += 2;
            break;
        }
        case JMP: // 读取FLAG寄存器值，如果为true，则跳转到JMP指令后的位置上
        {
            if (registers[FLAG]) {
                registers[IP] = instructions[registers[IP + 1]];
            }
            else
            {
                registers[IP]++;
            }
            break;
        }
        case JMPN: // 读取FLAG寄存器值，如果为true，则跳转到JMP指令后的位置上
        {
            if (registers[FLAG])
            {
                registers[IP] = instructions[registers[IP + 1]];
            }
            else
            {
                registers[IP]++;
            }
            break;
        }
        case HALT:
        {
            is_runing = false;
            registers[IP]++;
            printf("halt.\n");
            break;
        }
        default: {
            registers[IP]++;
        }
        }
}

void printRegisters() // 打印寄存器的值
{
    printf("Registers:\n");
    for (int i = 0; i < REG_SIZE; i++)
    {
        printf("%s: %+05d\n", registerToString(i), registers[i]);
    }

    printf("Stack:\n");
    for (int i = 0; i < STACK_SIZE; i++)
    {
        if (i % 10 == 0)
        {
            printf("\n");
            printf("%2d ", i);
        }
        printf("%+5d ", stack[i]);
    }
    printf("\n\n");
}

bool isZeroError(int operateNumber) // 判断除数是否为0
{
    if (operateNumber == 0)
        return true;
    return false;
}

bool isAccumulatorError(int accumulator)
{
    if (accumulator > HALT || accumulator < GPT)
        return true;
    return false;
}
